################################################################################
#
#Copyright (c) 2011, Petr Skramovsky
#All rights reserved.
#
#Redistribution and use in source and binary forms, with or without
#modification, are permitted provided that the following conditions are met:
#    * Redistributions of source code must retain the above copyright
#      notice, this list of conditions and the following disclaimer.
#    * Redistributions in binary form must reproduce the above copyright
#      notice, this list of conditions and the following disclaimer in the
#      documentation and/or other materials provided with the distribution.
#
#THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
#ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
#WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
#DISCLAIMED. IN NO EVENT SHALL Petr Skramovsky BE LIABLE FOR ANY
#DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
#(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
#LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
#ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
#(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
#SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#
################################################################################

from bsp_util import *
from bsp import *
from qio import *
import struct

class BSPReader( AReader ):

  def parse_lump( self, file ):
    l = Lump()
    l.offset, l.size = struct.unpack( "<2l", file.read( SIZE_LUMP )  )
    return l

  def parse_header( self, file ):
    assert file
    file.seek( 0 )
    h = Header()
    h.magic, h.version = struct.unpack( "<2l", file.read( SIZE_HEADER ) )
    for i in range( 0, LUMPS_COUNT ) :
      h.lumps[ i ] = self.parse_lump( file )
    return h

  def parse_entities( self, file, lump ):
    assert file
    file.seek( lump.offset )
    return struct.unpack( "<" + str( lump.size ) + "s", file.read( lump.size ) )[0]

  def parse_textures( self, file, lump ):
    assert file
    file.seek( lump.offset )
    assert not lump.size % SIZE_TEXTURE
    n = lump.size / SIZE_TEXTURE
    ts = [ Texture() for i in range( n ) ]
    for t in ts:
      t.name, t.flags, t.contents = struct.unpack( "<64s2l", file.read( SIZE_TEXTURE ) )
      t.name = t.name.rstrip(chr(0))
    return ts

  def parse_planes( self, file, lump ):
    assert file
    file.seek( lump.offset )
    assert not lump.size % SIZE_PLANE
    n = lump.size / SIZE_PLANE
    ps = [ Plane() for i in range( n ) ]
    for p in ps:
      p.normal = self.parse_farray( file, 3 )
      p.dist = self.parse_f( file )
    return ps

  def parse_nodes( self, file, lump ):
    assert file
    file.seek( lump.offset )
    assert not lump.size % SIZE_NODE
    n = lump.size / SIZE_NODE
    ns = [ Node() for i in range( n ) ]
    for nd in ns:
      nd.plane = self.parse_i( file )
      nd.children = self.parse_iarray( file, 2 )
      nd.mins = self.parse_iarray( file, 3 )
      nd.maxs = self.parse_iarray( file, 3 )
    return ns

  def parse_leafs( self, file, lump ):
    assert file
    file.seek( lump.offset )
    assert not lump.size % SIZE_LEAF
    n = lump.size / SIZE_LEAF
    ls = [ Leaf() for i in range( n ) ]
    for l in ls:
      l.cluster = self.parse_i(file)
      l.area = self.parse_i(file)
      l.mins = self.parse_iarray(file, 3)
      l.maxs = self.parse_iarray(file, 3)
      l.leafface = self.parse_i(file)
      l.n_leaffaces = self.parse_i(file)
      l.leafbrush = self.parse_i(file)
      l.n_leafbrushes = self.parse_i(file)
    return ls

  def parse_leaffaces( self, file, lump ):
    assert file
    file.seek( lump.offset )
    assert not lump.size % SIZE_LEAFFACE
    n = lump.size / SIZE_LEAFFACE
    ls = [ 0 ] * n
    for i in range( n ):
      ls[ i ] = self.parse_i(file)
    return ls

  def parse_leafbrushes( self, file, lump ):
    assert file
    file.seek( lump.offset )
    assert not lump.size % SIZE_LEAFBRUSH
    n = lump.size / SIZE_LEAFBRUSH
    ls = [ 0 ] * n
    for i in range( n ):
      ls[ i ] = self.parse_i(file)
    return ls

  def parse_models( self, file, lump ):
    assert file
    file.seek( lump.offset )
    assert not lump.size % SIZE_MODEL
    n = lump.size / SIZE_MODEL
    ms = [ Model() for i in range( n ) ]
    for m in ms:
      m.mins = self.parse_farray(file, 3)
      m.maxs = self.parse_farray(file, 3)
      m.face = self.parse_i(file)
      m.n_faces = self.parse_i(file)
      m.brush = self.parse_i(file)
      m.n_brushes = self.parse_i(file)
    return ms

  def parse_brushes( self, file, lump ):
    assert file
    file.seek( lump.offset )
    assert not lump.size % SIZE_BRUSH
    n = lump.size / SIZE_BRUSH
    bs = [ Brush() for i in range( n ) ]
    for b in bs:
      b.brushside = self.parse_i(file)
      b.n_brushsides = self.parse_i(file)
      b.texture = self.parse_i(file)
    return bs

  def parse_brushsides( self, file, lump ):
    assert file
    file.seek( lump.offset )
    assert not lump.size % SIZE_BRUSHSIDE
    n = lump.size / SIZE_BRUSHSIDE
    bs = [ BrushSide() for i in range( n ) ]
    for b in bs:
      b.plane = self.parse_i(file)
      b.texture = self.parse_i(file)
    return bs

  def parse_vertexes( self, file, lump ):
    assert file
    file.seek( lump.offset )
    assert not lump.size % SIZE_VERTEX
    n = lump.size / SIZE_VERTEX
    vs = [ Vertex() for i in range(n) ]
    for v in vs:
      v.position = self.parse_farray( file, 3 )
      v.texcoord = [ [ 0 ] * 2, [ 0 ] * 2 ]
      v.texcoord[ 0 ] = self.parse_farray( file, 2 )
      v.texcoord[ 1 ] = self.parse_farray( file, 2 )
      v.normal = self.parse_farray( file, 3 )
      v.color = self.parse_ubarray( file, 4 )
    return vs

  def parse_meshverts( self, file, lump ):
    file.seek( lump.offset )
    n = lump.size / SIZE_MESHVERT
    ms = [ 0 ] * n
    for i in range( 0, n ):
      ms[ i ] = self.parse_i(file)
    return ms
  
  def parse_effects( self, file, lump ):
    assert file
    file.seek( lump.offset )
    assert not lump.size % SIZE_EFFECT
    n = lump.size / SIZE_EFFECT
    es = [ Effect() for i in range( n ) ]
    for e in es:
      e.name, e.brush, e.unknown = struct.unpack( "<64s2l", file.read( SIZE_EFFECT ) )
      e.name = e.name.rstrip( chr( 0 ) )
    return es

  def parse_faces( self, file, lump ):
    assert file
    file.seek( lump.offset )
    assert not lump.size % SIZE_FACE
    n = lump.size / SIZE_FACE
    fs = [ Face() for i in range(n) ]
    for f in fs:
      f.texture, f.effect, f.type, f.vertex = self.parse_iarray( file,  4 )
      f.n_vertexes, f.meshvert, f.n_meshverts, f.lm_index = self.parse_iarray( file,  4 )
      f.lm_start = self.parse_iarray( file, 2 )
      f.lm_size = self.parse_iarray( file, 2 )
      f.lm_origin = self.parse_farray( file, 3 )
      f.lm_vecs[ 0 ] = self.parse_farray( file, 3 )
      f.lm_vecs[ 1 ] = self.parse_farray( file, 3 )
      f.normal = self.parse_farray( file, 3 )
      f.size = self.parse_iarray( file, 2 )
    return fs

  def parse_lightmaps( self, file, lump ):
    assert file
    file.seek( lump.offset )
    assert not lump.size % SIZE_LIGHTMAP
    n = lump.size / SIZE_LIGHTMAP
    ls = [[0]*LIGHTMAP_WIDTH*LIGHTMAP_HEIGHT] * n
    for l in ls:
      for x in range(LIGHTMAP_WIDTH):
        for y in range(LIGHTMAP_HEIGHT):
          l.append(self.parse_uc(file))
          l.append(self.parse_uc(file))
          l.append(self.parse_uc(file))
    return ls

  def parse_lightvols( self, file, lump ):
    assert file
    file.seek( lump.offset )
    assert not lump.size % SIZE_LIGHTVOL
    n = lump.size / SIZE_LIGHTVOL
    ls = [ LightVol() for i in range(n) ]
    for l in ls:
      l.ambient = self.parse_ubarray( file, 3 )
      l.directional = self.parse_ubarray( file, 3 )
      l.dir = self.parse_ubarray( file, 2 )
    return ls

  def parse_visdata( self, file, lump ):
    assert file
    file.seek( lump.offset )
    vd = Visdata()
    vd.n_vecs = self.parse_i(file)
    vd.sz_vecs = self.parse_i(file)
    vd.vecs = self.parse_ubarray(file, vd.n_vecs * vd.sz_vecs )
    return vd

  def read( self, file ):
    m = BSPModel()
    m.header = self.parse_header( file )
    m.entities = self.parse_entities( file, m.header.lumps[ LUMP_TYPE_ENTITIES ] )
    m.textures = self.parse_textures( file, m.header.lumps[ LUMP_TYPE_TEXTURES ] )
    m.planes = self.parse_planes( file, m.header.lumps[ LUMP_TYPE_PLANES ] )
    m.nodes = self.parse_nodes( file, m.header.lumps[ LUMP_TYPE_NODES ] )
    m.leafs = self.parse_leafs( file, m.header.lumps[ LUMP_TYPE_LEAFS ] )
    m.leaffaces = self.parse_leaffaces( file, m.header.lumps[ LUMP_TYPE_LEAFFACES ] )
    m.leafbrushes = self.parse_leafbrushes( file, m.header.lumps[ LUMP_TYPE_LEAFBRUSHES ] )
    m.models = self.parse_models( file, m.header.lumps[ LUMP_TYPE_MODELS ] )
    m.brushes = self.parse_brushes( file, m.header.lumps[ LUMP_TYPE_BRUSHES ] )
    m.brushsides = self.parse_brushsides( file, m.header.lumps[ LUMP_TYPE_BRUSHSIDES ] )
    m.vertexes = self.parse_vertexes( file, m.header.lumps[ LUMP_TYPE_VERTEXES ] )
    m.meshverts = self.parse_meshverts( file, m.header.lumps[ LUMP_TYPE_MESHVERTS ] )
    m.effects = self.parse_effects( file, m.header.lumps[ LUMP_TYPE_EFFECTS ] )
    m.faces = self.parse_faces( file, m.header.lumps[ LUMP_TYPE_FACES ] )
    m.lightmaps = self.parse_lightmaps( file, m.header.lumps[ LUMP_TYPE_LIGHTMAPS ] )
    m.lightvols = self.parse_lightvols( file, m.header.lumps[ LUMP_TYPE_LIGHTVOLS ] )
    #m.visdata = self.parse_visdata( file, m.header.lumps[ LUMP_TYPE_VISDATA ] )
    return m
